package cn.xiongxf.service.impl;

import cn.xiongxf.domain.AlipayInfo;
import cn.xiongxf.domain.PayFlow;
import cn.xiongxf.domain.PayOrder;
import cn.xiongxf.dto.AlipayNotifyDTO;
import cn.xiongxf.dto.PayParamDTO;
import cn.xiongxf.dto.PayResult2MQDTO;
import cn.xiongxf.exception.GlobalCustomException;
import cn.xiongxf.mapper.PayOrderMapper;
import cn.xiongxf.service.IAlipayInfoService;
import cn.xiongxf.service.IPayFlowService;
import cn.xiongxf.service.IPayOrderService;
import cn.xiongxf.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xiongxiaofu
 * @since 2022-08-28
 */
@Service
@Slf4j
@Transactional
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {
    @Autowired
    private IAlipayInfoService alipayInfoService;
    @Autowired
    private RocketMQTemplate mqTemplate;
    @Autowired
    private IPayFlowService payFlowService;

    // 抽取方法：根据订单号查询支付单
    private PayOrder selectByOrderNo(String orderNo) {
        return selectOne(new EntityWrapper<PayOrder>().eq("order_no", orderNo));
    }

    /**
     * 发起支付申请
     */
    @Override
    public String apply(PayParamDTO dto) {
        // 1.参数校验：@valid

        // 2.条件校验：支付状态必须是待支付
        PayOrder payOrder = selectByOrderNo(dto.getOrderNo());
        AssertUtil.isNotNull(payOrder, "非法请求！");
        AssertUtil.isEquals(payOrder.getPayStatus(), PayOrder.STATE_WAIT_PAY, "订单状态异常！");

        // 解决支付超时取消订单和支付宝异步回调的bug
        // 判断支付单下单时间，如果跟当前时间超过20分钟，不准支付  10/8 30/20
        if((new Date().getTime() - payOrder.getCreateTime().getTime()) / 1000 / 60 > 20){
            throw new GlobalCustomException("支付超时，请重新下单！");
        }

        // 3.查询配置，构建支付宝配置
        AlipayInfo alipayInfo = alipayInfoService.selectList(null).get(0);
        // 支付宝配置
        Factory.setOptions(getOptions(alipayInfo));

        // 4.构建支付
        AlipayTradePagePayResponse response = null;
        // 处理响应或异常
        try {
            String returnURL = StringUtils.isNotEmpty(dto.getCallUrl()) ? dto.getCallUrl() : alipayInfo.getReturnUrl();
            // 发起支付
            response = Factory.Payment.Page().pay(
                    payOrder.getSubject(),
                    payOrder.getOrderNo(),
                    payOrder.getAmount().toString(),
                    returnURL
            );
            System.out.println(response.getBody());
            // 断言支付响应成功
            AssertUtil.isTrue(ResponseChecker.success(response), "支付失败！");

            // 更新支付订单信息：修改支付单状态为支付中，设置更新时间
            payOrder.setPayStatus(PayOrder.STATE_PAYMENTS);
            payOrder.setUpdateTime(new Date());
            updateById(payOrder);

            // 返回响应体
            return response.getBody();  // HTML代码片段
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalCustomException("支付失败，系统异常！");
        }
    }

    // 抽取方法：支付宝配置
    private Config getOptions(AlipayInfo alipayInfo) {
        Config config = new Config();
        // 协议
        config.protocol = alipayInfo.getProtocol();
        // 网关
        config.gatewayHost = alipayInfo.getGatewayHost();
        // 签名算法
        config.signType = alipayInfo.getSignType();
        // 应用ID
        config.appId = alipayInfo.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        // 应用私钥：加签
        config.merchantPrivateKey = alipayInfo.getMerchantPrivateKey().replace("\\", "");  // session_key中可能出现\这个符号，会导致invalid错误
        // 支付宝公钥：验签
        config.alipayPublicKey = alipayInfo.getAlipayPublicKey().replace("\\", "");
        // 可设置异步通知接收服务地址（可选）
        config.notifyUrl = alipayInfo.getNotifyUrl();
        return config;
    }



    /**
     * 支付宝异步回调接口
     * 如果不需要支付宝继续调用，返回success
     */
    @Override
    public String alipayNotify(AlipayNotifyDTO dto) {
        // 1.参数校验（支付宝给的参数一般没问题）
        String out_trade_no = dto.getOut_trade_no();
        String total_amount = dto.getTotal_amount();
        String trade_status = dto.getTrade_status();
        AssertUtil.isNotEmpty(out_trade_no, "订单号无效！");

        // 2.验证签名
        Map<String, String> paramMap = JSON.parseObject(JSON.toJSONString(dto), Map.class);
        Boolean signVerified = false;
        try {
            signVerified = Factory.Payment.Common().verifyNotify(paramMap);
            AssertUtil.isTrue(signVerified, "验签失败！");
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalCustomException("验签失败！");
        }
        AssertUtil.isFalse(dto.isTradeWit(), "待支付，不处理！");

        // 3.判断支付结果是否已处理：支付订单不是支付中，支付订单已经被处理过，无需再处理
        PayOrder payOrder = selectByOrderNo(out_trade_no);
        AssertUtil.isNotNull(payOrder, "订单号无效！");
        AssertUtil.isEquals(payOrder.getPayStatus(), PayOrder.STATE_PAYMENTS, "订单号已被处理！");

        // 如果支付宝扣款成功，而平台订单是超时自动取消或用户手动取消状态，就退款
        if(dto.isTradeSuccess() &&
                (payOrder.getPayStatus().equals(PayOrder.STATE_OVERTIME_CANCEL)
                        || payOrder.getPayStatus().equals(PayOrder.STATE_USER_CANCEL))){
            try {
                AlipayTradeRefundResponse response = Factory.Payment.Common()
                        .refund(out_trade_no, payOrder.getAmount().toString());
                log.info("退款：{}" ,response);
                return "success";
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        AssertUtil.isEquals(payOrder.getPayStatus(),PayOrder.STATE_PAYMENTS,"订单已被处理!");

        // 4.核对订单金额，买家，卖家等
        BigDecimal totalAmount = new BigDecimal(total_amount);
        if (payOrder.getAmount().compareTo(totalAmount) != 0) throw new GlobalCustomException("金额异常！");

        // 5.MQ事务监听器：
        // 修改支付订单
        payOrder.setPayStatus(dto.isTradeSuccess() ? PayOrder.STATE_SUCCESS_PAY : PayOrder.STATE_OVERTIME_CANCEL);
        Date now = new Date();
        payOrder.setUpdateTime(now);
//        updateById(payOrder);

        // 保存支付流水
        PayFlow payFlow = new PayFlow();
        payFlow.setNotifyTime(now);
        payFlow.setSubject(dto.getSubject());
        payFlow.setOutTradeNo(out_trade_no);
        payFlow.setTotalAmount(totalAmount);
        payFlow.setTradeStatus(dto.getTrade_status());
        payFlow.setCode(dto.getCode());
        payFlow.setMsg(dto.getMsg());
        payFlow.setPaySuccess(dto.isTradeSuccess());
//        insert(payFlow);

        // 6.构建支付结果对象，发MQ事务广播消息 =============================================================
        // 消息内容：支付结果
        PayResult2MQDTO payResult2MQDTO = new PayResult2MQDTO(
                out_trade_no, dto.isTradeSuccess(), payOrder.getExtParams());
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payResult2MQDTO)).build();

        // 扩展参数：支付单，支付流水
        HashMap<String, Object> map = new HashMap<>();
        map.put("payOrder", payOrder);
        map.put("payFlow", payFlow);

        TransactionSendResult result = mqTemplate.sendMessageInTransaction(
                "tx-producer-pay-result",
                "topic-payresult:tags-paysult",
                message,  // 消息内容：支付结果
                map  // 扩展参数：支付单，支付流水
        );
        if (result.getLocalTransactionState() != LocalTransactionState.COMMIT_MESSAGE
                || result.getSendStatus() != SendStatus.SEND_OK) throw new GlobalCustomException("事务消息发送失败！");

        return "success";
    }


    /**
     * 更新支付单,保存支付流水
     */
    @Override
    public void updateOrderAndFlow(PayOrder payOrder, PayFlow payFlow){
        // 校验支付单
//        PayOrder payOrderDB = selectByOrderNo(payOrder.getOrderNo());  // 和传参的地址一样，换ID查询
        PayOrder payOrderDB= selectById(payOrder.getId());
        AssertUtil.isNotNull(payOrderDB, "支付单无效！");
        AssertUtil.isEquals(payOrderDB.getPayStatus(), PayOrder.STATE_PAYMENTS, "支付单已被修改！");

        // 更新支付单
        updateById(payOrder);
        // 保存支付流水
        payFlowService.insert(payFlow);
    }


    /**
     * MQ延迟消息，根据订单号 自动取消超时支付单
     */
    @Override
    public void cancelOrder(String orderNo) {
        PayOrder payOrder = selectByOrderNo(orderNo);
        AssertUtil.isNotNull(payOrder,"支付单无效！");

        if(payOrder.getPayStatus().equals(PayOrder.STATE_WAIT_PAY) || payOrder.getPayStatus().equals(PayOrder.STATE_PAYMENTS)){
            // 待支付，支付中都要取消
            payOrder.setPayStatus(PayOrder.STATE_OVERTIME_CANCEL);
            payOrder.setUpdateTime(new Date());
            updateById(payOrder);
            log.info("支付单被取消： {}", payOrder);

            // 关闭支付宝支付交易
            try {
                AlipayTradeCloseResponse response = Factory.Payment.Common().close(orderNo);
                log.info("取消支付宝订单：{}, {}, {}", response.getCode(), response.getSubMsg(), response.getMsg());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



}
